#!/usr/bin/env python

import dbus
import subprocess

'''For avoiding name clashes, we will not import 
call, but compiz_call instead'''
#TODO: Add stuff for plugin listing and such
__all__ = ['compiz_call', 'get_root_win_id', 'compiz_list']

def destrify(s):
    '''Attempt to turn a string into an int, float, or bool
    depending on what kind of data it is'''
    booldict = {'true': True, 'false': False}
    value = None
    try:
        i = int(s, 0)
    except ValueError:
        try:
            f = float(s)
        except ValueError:
            value = booldict.get(s.lower(), s)
        else:
            value = f
    else:
        value = i

    return value

service = interface = 'org.freedesktop.compiz'

session_bus = dbus.SessionBus()

def get_root_win_id():
    '''Function to return the root window id as an int'''
    try:
        root_win = subprocess.Popen(['xwininfo', '-root'],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]

    except OSError:
        raise SystemExit('Error: xwininfo not present')

    try:
        root_win_id = int(root_win.split()[3], 0)

    except IndexError:
        raise SystemExit('Error: unexpectedly short output from xwininfo')

    except ValueError:
        raise SystemExit('Error: unable to convert "%s" to int', rootwin.split()[3])

    return int(root_win_id)

def parse_args(*argv):
    '''Function to parse the dbus arguments'''
    if len(argv) < 2:
        raise SystemExit('Error: Too few arguments, you must have at least 2: The plugin name and an action, for instance, scale and initiate_key')

    plugin = argv[0]
    '''This will decide whether we want to call the method,
    or just return a list of its actions'''
    try:
        action = argv[1]
    except:
        pass

    args = ['root', get_root_win_id()]

    extra_len = len(argv[3:])
    if extra_len >= 2 and extra_len % 2 == 0:
        for k, v in zip(argv[3::2], argv[4::2]):
            args.append(k)
            args.append(destrify(v))
        
    return [plugin, action, args]

def dbus_call(plugin, action, args):
    '''function to actually make the dbus call'''
    proxy = session_bus.get_object(service,
	    '/org/freedesktop/compiz/%s/allscreens/%s' %(plugin, action))
    obj = dbus.Interface(proxy, interface)
    obj.activate(*args)

def call(plugin, *args):
    '''Function that abstracts all the previous functions
    into one easy call.  To initiate the scale plugin, do
    as follows(assuming you imported it with import compiz
    and not from compiz import call or such):

    compiz.call('scale', 'initiate_key')

    its that simple
    '''
    dbus_call(*parse_args(plugin, *args))

def list(plugin):
    '''Lists all the actions of a given plugin'''
    proxy = session_bus.get_object(service, 
        '/org/freedesktop/compiz/%s/allscreens' %(plugin))
    obj = dbus.Interface(proxy, interface)
    return '\n'.join(obj.list())

'''For those of you that insist on importing this with
from compiz import call or from compiz import *, 
this will avoid name clashed and make code more readable.
It is just a new name for call.  When you import this module
with from compiz import * this and get_root_win_id are the
only things imported
'''
compiz_call = lambda plugin, *args: call(plugin, *args)

'''Same purpose as the above one, just for listing, 
not calling
'''
compiz_list = lambda plugin: list(plugin)

if __name__ == '__main__':
    '''Allow them to do cool stuff:P  They can
    call a plugin from the command line like this:
    
    ./compiz.py scale initiate_key

    or, they could list all actions, like this:
    
    ./compiz.py scale
    '''
    import sys
    if len(sys.argv) == 2:
        #The user wants to list actions, not call
        print list(sys.argv[1])
    elif len(sys.argv) > 2:
        #The user wants to call something
        args = sys.argv[2:]
        call(sys.argv[1], *args)
    else:
        print 'What do you think I am?  A mind reader?\
 Pass some arguments for goodness sake!'
